package com.youlian.cloud.log.asepct;

import com.alibaba.fastjson.JSON;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import com.youlian.cloud.core.bean.ExceptionHandle;
import com.youlian.cloud.core.bean.Info;
import com.youlian.cloud.core.exception.BusinessException;
import com.youlian.cloud.core.exception.CoreException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * service业务层Cat日志拦截器
 *
 * @author : liukx
 * @create : 2018/5/31 15:45
 * @email : liukx@elab-plus.com
 */
public class CatAspect {

    private final Logger logger = LoggerFactory.getLogger(CatAspect.class);

    /**
     * 字符串输出规则
     *
     * @param str
     * @return
     */
    public static String logOut(String str) {
        if (StringUtils.isEmpty(str)) {
            return "null";
        }

        if (str.length() > 500) {
            return str.substring(0, 500) + " 省略...";
        }

        return str;
    }

    /**
     * 环绕通知方法
     *
     * @param pjp
     * @throws Exception
     */
    public Object aroundMethod(ProceedingJoinPoint pjp) throws Exception {
        Object proceed = null;
        MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
        Method method = joinPointObject.getMethod();

        // 获取参数的名称
        Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
        String methodName = pjp.getSignature().getName();
        Class<?> classTarget = pjp.getTarget().getClass();
        Method objMethod = classTarget.getMethod(methodName, par);

        // 注解是否存在
        boolean annotationExist = objMethod.isAnnotationPresent(ExceptionHandle.class);

        // 构建一个类和方法的路径
        String value = pjp.getTarget().getClass().getSimpleName() + "." + methodName;
        String fullMethodName = value;
        if (annotationExist) {
            String username = objMethod.getAnnotation(ExceptionHandle.class).username();
            if (!StringUtils.isEmpty(username)) {
                value = username;
                logger.debug("--------------------进入负责人[" + value + "]区域------------------------");
            }
        }
        Transaction t = Cat.newTransaction(value, fullMethodName);
        logger.info(" 开始执行方法 " + method.getName() + " 参数:" + logOut(JSON.toJSONString(pjp.getArgs())));
        try {
            proceed = pjp.proceed();
            t.setStatus(Transaction.SUCCESS);
        } catch (Throwable e) {

            exceptionProcess(e);

            t.setStatus(e);
            logger.error("[" + value + "]", e);
            throw new RuntimeException(e.getMessage());
        } finally {
            if (proceed instanceof Info) {
                Info info = (Info) proceed;
                String currentMessageId = Cat.getCurrentMessageId();
                Map<String, Object> extension = info.getExtension();
                if (extension == null) {
                    extension = new HashMap<String, Object>(8);
                    info.setExtension(extension);
                }
                info.getExtension().put("messageId", currentMessageId);
                logger.info("出参结果 : " + info.isSuccess() + " 消息内容 : " + info.getMessage());
            }
            logger.info(" 结束执行方法 " + method.getName());
            long start = System.currentTimeMillis();
            t.complete();
            long time = System.currentTimeMillis() - start;
            logger.info(" CAT Service 拦截器事物提交时间 : " + time);
        }
        return proceed;
    }

    /**
     * 业务范围内的异常
     *
     * @param e
     */
    private void exceptionProcess(Throwable e) {
        if (e instanceof CoreException) {
            logger.info(e.getMessage());
            throw new CoreException(((CoreException) e).getErrorCode(), e.getMessage());
        }
        if (e instanceof BusinessException) {
            logger.info(e.getMessage());
            throw new BusinessException(((BusinessException) e).getErrorCode(), e.getMessage());
        }
    }

}
